﻿using Microsoft.Extensions.Logging;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Service.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace SimulationCenter.Servers
{
    public class ServerManager : IDisposable
    {
        private readonly ConcurrentDictionary<string, AppiumLocalService> _localServices
            = new ConcurrentDictionary<string, AppiumLocalService>();

        private readonly ILogger<ServerManager> _logger;

        public ServerManager(ILogger<ServerManager> logger)
        {
            _logger = logger;
        }

        public AppiumLocalService GetOrStart(string device)
        {
            if (!_localServices.TryGetValue(device, out var localService))
            {
                localService = CreateLocalService(device);

                if (_localServices.TryAdd(device, localService))
                {
                    //localService.Start();
                }
            }
            else
            {
                if (localService == null || !localService.IsRunning)
                {
                    localService = CreateLocalService(device);

                    //localService.Start();
                }
            }

            return localService;
        }

        public void Dispose()
        {
            foreach (var localService in _localServices.Values)
            {
                localService?.Dispose();
            }
        }

        #region privates

        private AppiumLocalService CreateLocalService(string device)
        {
            var optionCollector = new OptionCollector();
            optionCollector.AddArguments(GeneralOptionList.AsyncTrace());
            optionCollector.AddArguments(GeneralOptionList.OverrideSession());
            optionCollector.AddArguments(GeneralOptionList.LogTimeStamp());
            optionCollector.AddArguments(GeneralOptionList.LogLevel("info"));

            optionCollector.AddArguments(new KeyValuePair<string, string>("--relaxed-security", string.Empty));
            optionCollector.AddArguments(new KeyValuePair<string, string>("--bootstrap-port", $"{GetFreePort()}"));
            optionCollector.AddArguments(new KeyValuePair<string, string>("--callback-port", $"{GetFreePort()}"));

            var localService = new AppiumServiceBuilder()
                .UsingPort(GetFreePort())
                .WithArguments(optionCollector)
                .Build();

            localService.OutputDataReceived += (sender, e) =>
            {
                _logger.LogInformation($"{device} -> {e.Data}");
            };

            return localService;
        }

        #endregion

        public static int GetFreePort()
        {
            Socket socket = null;
            try
            {
                socket = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                socket.Bind(new IPEndPoint(IPAddress.Any, 0));
                return ((IPEndPoint)socket.LocalEndPoint).Port;
            }
            finally
            {
                socket?.Dispose();
            }
        }
    }
}
